/*Copyright (c) 2011 Guillermo Quiros

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the"Software"), 
to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.ogelframework.registry
{
	import org.ogelframework.core.Const;
	import org.ogelframework.core.Dispatcher;
	import org.ogelframework.core.OgelTools;
	import org.ogelframework.helpers.Watcher;
	
	import flash.system.ApplicationDomain;
	import flash.utils.Dictionary;
	import flash.utils.getDefinitionByName;
	import flash.utils.getQualifiedClassName;
	import flash.utils.getQualifiedSuperclassName;

	public class Registry 
	{
		
		
		private var queue:Array;
		
		private var _entities:Dictionary;
	
		private var _dependencies:Dependencies;
		private var _mainEntity:Entity;
		
		public function Registry()
		{
			_entities=new Dictionary();
			_dependencies=new Dependencies();
			queue=[];
		}
		
	
		public function destroy():void
		{
		}
		
		public function registerMainEntity(instance:Object):void
		{
			var className:String = getQualifiedClassName( instance );
			var clazz:Class = getDefinitionByName( className ) as Class;
			_entities['this']=new Entity("this",clazz,Const.EN_CMP);
			_entities['this'].instance=instance;
			_mainEntity=_entities['this'];
			register(_entities['this']);
		}

		public function getEntity(key:String):Entity
		{
			var simpleKey:String;
			var arg:Array=key.split('.');
			simpleKey=arg[0];
			return entities[simpleKey] as Entity
		}
		
		/*public function addEntity(id:String,clazz:Class,type:String):void
		{
			_entities[id]=new Entity(id,clazz,type);
		}*/
		
		
		public function register(entity:Entity):void
		{
			trace("REGISTER"+getQualifiedClassName(entity.clazz))
			//Read system
			readEntity(entity, OgelTools.instance.getAnotations(entity.clazz,"sys"),Const.EN_SYS);
			//Read cmp
			readEntity(entity, OgelTools.instance.getAnotations(entity.clazz,"cmp"),Const.EN_CMP);
			//Read injection
			readInject(entity, OgelTools.instance.getAnotations(entity.clazz,"inject"));
			//read handles
			readHandlers(entity, OgelTools.instance.getAnotations(entity.clazz,"handle"));
			//read link
			readLink(entity, OgelTools.instance.getAnotations(entity.clazz,"link"));
			//Inject dispatcher
			injectDispatcher(entity, OgelTools.instance.getAnotations(entity.clazz,"dispatcher"));
			//Inject Injector :)
			injectInjector(entity, OgelTools.instance.getAnotations(entity.clazz,"injector"));
			//Inject Registry
			injectRegistry(entity, OgelTools.instance.getAnotations(entity.clazz,"registry"));
			//Process queue
			processQueue()
		
		}
		
		private function readEntity(entity:Entity,metadata:Array,type:String):void
		{
			var clazz:Class;
			var source:String="";
			var destination:String="";
			var id:String;
			var dependency:String;
			for (var i:int = 0; i < metadata.length; i++)
			{
				source="";
				destination="";
				clazz=getClassDefinition(ApplicationDomain.currentDomain ,metadata[i].parent().@type );
				id=metadata[i].parent().@name;
				entities[id]=new Entity(id,clazz,type);
				
				source = metadata[i].arg.(@key == "destination").@value;
				if (source=="")
					source=metadata[i].parent().@name;
				
				if (destination=="")
					destination=entity.id+'.'+metadata[i].parent().@name;
				if(type==Const.EN_SYS)
				{
					queue.push(entities[id]);
					dependency="injection#"+source+"#"+destination;
				}
				else
				{
					dependency="cmpInjection#"+source+"#"+destination;
				}
				trace("readEntity"+dependency);
				_dependencies.addDependency(source,destination,dependency); 		
			
						
			}
		}
		
		private function readInject(entity:Entity,metadata:Array):void
		{
			var source:String="";
			var destination:String="";
			var dependency:String;
			for (var i:int = 0; i < metadata.length; i++)
			{
				source="";
				destination="";
				source = metadata[i].arg.(@key == "source").@value;
				if (source=="")
					source=destination=metadata[i].parent().@name;;
				
				destination = metadata[i].arg.(@key == "destination").@value;
				if (destination=="")
					destination=entity.id+'.'+metadata[i].parent().@name;
				
				dependency="injection#"+source+"#"+destination;
				_dependencies.addDependency(source,destination,dependency) 
			}
		}
		
		private function readHandlers(entity:Entity,metadata:Array):void
		{
			var event:String="";
			var source:String="";
			var destination:String="";
			var dependency:String="";
			for (var i:int = 0; i < metadata.length; i++)
			{
				source="";
				destination="";
				event = metadata[i].arg.(@key == "event").@value;
				source = metadata[i].arg.(@key == "source").@value;
				if (source=="")
					source="this";
				destination = metadata[i].arg.(@key == "destination").@value;
				if (destination=="")
					destination=entity.id+"."+metadata[i].parent().@name;
				dependency="handler#"+source+"#"+destination+"#"+event+"#";
				trace(dependency);
				_dependencies.addDependency(source,destination,dependency) 
			} 
		}
		
		private function readLink(entity:Entity,metadata:Array):void
		{
			var source:String="";
			var destination:String="";
			var dependency:String="";
			for (var i:int = 0; i < metadata.length; i++)
			{
				source="";
				destination="";
				source = metadata[i].arg.(@key == "source").@value;
				if (source=="")
					source="this";
				destination = metadata[i].arg.(@key == "destination").@value;
				if (destination!="")
					destination=metadata[i].parent().@name+"."+destination
				else
					destination=entity.id+"."+metadata[i].parent().@name;      
				dependency="link#"+source+"#"+destination;
				_dependencies.addDependency(source,destination,dependency);
				//dependency=Const.INJECTION+"#"+source+"#"+destination;
				//_dependencies.addDependency(source,destination,dependency);
			} 
		}
		
		private function injectDispatcher(entity:Entity,metadata:Array):void
		{
			var clazz:Class;
			var destination:String="";
			for (var i:int = 0; i < metadata.length; i++)
			{
				destination="";
				destination = metadata[i].arg.(@key == "destination").@value;
				if (destination=="")
					destination=entity.id+"."+metadata[i].parent().@name;                        
				_dependencies.addDependency("this",metadata[i].parent().@name,"dispatcher#this#"+destination) 
				trace("dispatcher"+metadata[i].parent().@name+"type"+metadata[i].parent().@type )
			} 
		}
		
		private function injectInjector(entity:Entity,metadata:Array):void
		{
			var clazz:Class;
			var destination:String="";
			for (var i:int = 0; i < metadata.length; i++)
			{
				destination="";
				destination = metadata[i].arg.(@key == "destination").@value;
				if (destination=="")
					destination=entity.id+"."+metadata[i].parent().@name;                        
				_dependencies.addDependency("this",metadata[i].parent().@name,"injector#this#"+destination) 
				trace("injector"+metadata[i].parent().@name+"type"+metadata[i].parent().@type )
			} 
		}
		
		private function injectRegistry(entity:Entity,metadata:Array):void
		{
			var clazz:Class;
			
			for (var i:int = 0; i < metadata.length; i++)
			{
				_dependencies.addDependency("registry",metadata[i].parent().@name,"registry") 
				trace("registry"+metadata[i].parent().@name+"type"+metadata[i].parent().@type )
			} 
		}

		private function processQueue():void
		{
			while(queue.length>0)
			{
				register(queue.pop())
			}
		
		}

		//TOOLS//
		private  function getClassDefinition( domain:ApplicationDomain, name:String ):Class
		{
			try
			{
				return domain.getDefinition( name ) as Class;
			}
			catch( e:ReferenceError )
			{
			}
			
			return null;
		}	

		public function get entities():Dictionary
		{
			return _entities;
		}

		public function get dependencies():Dependencies
		{
			return _dependencies;
		}

		public function get mainEntity():Entity
		{
			return _mainEntity;
		}

	}
}